﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Configuration;
using System.Web.Mvc;
using System.Web.Security;
using HtmlAgilityPack;
using SelfMadeMillionaire.Core;
using SelfMadeMillionaire.Core.Entities;
using SelfMadeMillionaire.Core.Enums;
using SelfMadeMillionaire.Core.Services;
using SelfMadeMillionaire.Web.Infrastructure.Attributes;
using SelfMadeMillionaire.Web.Infrastructure.ViewModels.Account;
using SelfMadeMillionaire.Web.Infrastructure.ViewModels.PayPal;

namespace SelfMadeMillionaire.Web.Infrastructure.Controllers
{
    [AjaxAuthorizeAttribute]
	public class ProfileController : BaseController
	{
		private readonly IPaymentService _paymentService;
		private readonly ISubscriberService _subscriberService;
        private readonly IPromotionService _promotionService;
        private readonly IEmailService _emailService;
        private readonly ILogService _logService;
        private readonly IUserPortfolioService _userPortfolioService;
        private readonly IUserSymbolService _userSymbolService;
        private readonly ISymbolOptionsHistoryService _symbolOptionsHistoryService;


        public ProfileController(ISubscriberService subscriberService, IPaymentService paymentService, IPromotionService promotionService, IEmailService emailService, ILogService logService, IUserPortfolioService userPortfolioService, IUserSymbolService userSymbolService, ISymbolOptionsHistoryService symbolOptionsHistoryService)
		{
			_subscriberService = subscriberService;
			_paymentService = paymentService;
            _promotionService = promotionService;
            _emailService = emailService;
            _logService = logService;
            _userPortfolioService = userPortfolioService;
            _userSymbolService = userSymbolService;
            _symbolOptionsHistoryService = symbolOptionsHistoryService;
		}

        public ActionResult IsAuthorize()
        {
            return Json(new { success = true }, JsonRequestBehavior.AllowGet);
        }
       

		public ActionResult Index(int? sts)
		{
            ClearProductTypeInSession();

            bool justActivated = Session["JustActivated"] != null;
            Session["JustActivated"] = null;

            ProfileViewModel model = new ProfileViewModel();
            model.SubscriptionTabSelected = justActivated || (sts.HasValue && sts.Value == 1);

            return View(model);
		}

        [HttpGet]
        public ActionResult Activated()
        {
            Session["JustActivated"] = "JustActivated";

            Subscriber subscriber = _subscriberService.Get(User.Identity.Name);
            _emailService.SendWelcomeLetter(subscriber);

            return View();
        }

		[HttpGet]
		public PartialViewResult PersonalInfo()
		{
			Subscriber subscriber = _subscriberService.Get(User.Identity.Name);

			var model = new PersonalInfoViewModel
			{
				FirstName = subscriber.FirstName,
				LastName = subscriber.LastName,
				Email = subscriber.Email
			};

            if (Session["AuthDataUpdated"] != null)
            {
                Session["AuthDataUpdated"] = null;
                model.IsSuccess = true;
            }

			return PartialView(model);
		}

        [HttpPost]
        public PartialViewResult PersonalInfo(PersonalInfoViewModel model)
        {
            Subscriber subscriber = _subscriberService.Get(User.Identity.Name);
            if (_subscriberService.IsExists(model.Email, subscriber.ID))
                ModelState.AddModelError("Email", "Email already exists!");


            if (ModelState.IsValid)
            {
                bool emailChanged = (subscriber.Email != model.Email) || (subscriber.FirstName != model.FirstName) || (subscriber.LastName != model.LastName);
                string oldEmail = subscriber.Email;

                subscriber.FirstName = model.FirstName;
                subscriber.LastName = model.LastName;
                subscriber.Email = model.Email;
                _subscriberService.Update(subscriber);

                if (emailChanged)
                {
                    ReAuthenticate(oldEmail, model.Email, model.FirstName, model.LastName);
                    Session["AuthDataUpdated"] = 1;
                }

                model.IsSuccess = true;
            }

            return PartialView(model);
        }

		[HttpGet]
		public PartialViewResult SubscriptionInfo()
		{
			Subscriber subscriber = _subscriberService.Get(User.Identity.Name);

			var model = new SubscriptionInfoViewModel
			{
				IsMMM =(subscriber.ProductType == (byte) ProductType.MaggiesMoneyMountain),
				IsSMM =(subscriber.ProductType == (byte) ProductType.SelfMadeMillionaire),
				IsMMMSubscribe = !subscriber.MmmUnsubscribed,
				IsSMMSubscribe = !subscriber.SmmUnsubscribed,
				SubscriptionLevel = (SubscriptionType) (subscriber.SubscriptionLevel ?? 0),
				Balance = _paymentService.GetBalance(subscriber.Email),
				ExperationDate = subscriber.EndDate.HasValue ? subscriber.EndDate.Value : subscriber.CreatedDate.AddYears(1),
				ProductList = _subscriberService.GetProducts(subscriber)
			};

			return PartialView(model);
		}

        public PartialViewResult Portfolios(int selectedPortfolioId)
        {
            Subscriber subscriber = _subscriberService.Get(User.Identity.Name);
            PortfoliosViewModel model = new PortfoliosViewModel();
            model.Portfolios = _userPortfolioService.GetAll(subscriber.ID).OrderBy(x => x.PortfolioName).ToList();

            bool isFound;
            if (selectedPortfolioId > 0)
            {
                UserPortfolio selected = _userPortfolioService.Get(selectedPortfolioId);

                if (selected != null)
                {
                    model.SelectedPortfolioId = selectedPortfolioId;
                    model.SelectedPortfolioName = selected.PortfolioName;

                    List<UserSymbol> items = selected.Symbols.OrderBy(x => x.SymbolCode).ToList();
                    foreach (UserSymbol item in items)
                        model.InfoList.Add(ParseCompany(model.SelectedPortfolioId, item.SymbolCode, out isFound));

                    InfoList.Clear();
                }
                else
                {
                    model.SelectedPortfolioId = -1;
                    model.SelectedPortfolioName = "New Watch List";
                    foreach (CompanyInfo item in InfoList)
                        model.InfoList.Add(ParseCompany(model.SelectedPortfolioId, item.Symbol, out isFound));
                }
            }
            else
            {
                model.SelectedPortfolioId = -1;
                model.SelectedPortfolioName = "New Watch List";
                foreach (CompanyInfo item in InfoList)
                    model.InfoList.Add(ParseCompany(model.SelectedPortfolioId, item.Symbol, out isFound));
            }
            
            model.SymbolsList = "";
            model.InfoList.ForEach(x => model.SymbolsList += x.Symbol + ",");

            if (!string.IsNullOrEmpty(model.SymbolsList))
                model.Hystory = _symbolOptionsHistoryService.Search(model.IsCallsFilter, model.DaysInTradeFilter, model.MinDiscountFilter, model.MinAnnualReturnFieldFilter, model.MinAnnualReturnFilter, model.MinProbabilityFilter, model.SymbolsList, model.SortOrderSymbolOptionsHystory);

            if (TempData["NotFoundSymbol"] != null)
                if (Convert.ToBoolean(TempData["NotFoundSymbol"]))
                {
                    model.NotFoundSymbol = true;
                }

            return PartialView(model);
        }

        [HttpGet]
        public ActionResult PortfolioDialog(int id)
        {
            PortfolioDialogModel model = new PortfolioDialogModel();
            model.Id = id;
            if (id > 0)
                model.Name = _userPortfolioService.Get(id).PortfolioName;

            return PartialView("PortfolioDialog", model);
        }

        [HttpPost]
        public ActionResult PortfolioDialog(PortfolioDialogModel model)
        {
            if (ModelState.IsValid)
            {
                if (_userPortfolioService.GetAll(x => x.PortfolioName.ToLower() == model.Name.ToLower()).Any())
                    ModelState.AddModelError("Name", "Name already exists");
                else
                {
                    bool isNew = (model.Id <= 0);
                    UserPortfolio dbItem;
                    if (isNew)
                    {
                        dbItem = new UserPortfolio();
                        dbItem.Subscriber = _subscriberService.Get(User.Identity.Name);
                        dbItem.Symbols = new List<UserSymbol>();
                    }
                    else
                        dbItem = _userPortfolioService.Get(model.Id);

                    dbItem.PortfolioName = model.Name;
                    if (isNew)
                    {
                        foreach (CompanyInfo item in InfoList)
                        {
                            UserSymbol dbItemSub = new UserSymbol();
                            dbItemSub.SymbolCode = item.Symbol;
                            dbItemSub.Status = 1;
                            dbItem.Symbols.Add(dbItemSub);
                        }
                        InfoList.Clear();
                    }

                    if (isNew)
                        _userPortfolioService.Add(dbItem);
                    else
                        _userPortfolioService.Update(dbItem);

                    return Json(new { success = true, id = dbItem.ID }, JsonRequestBehavior.AllowGet);
                }
            }

            return PartialView(model);
        }

        [HttpPost]
        public ActionResult DeletePortfolio(int id)
        {
            UserPortfolio dbItem = _userPortfolioService.Get(id);
            if (dbItem != null)
            {
                dbItem.Symbols.ToList().ForEach(x => _userSymbolService.Remove(x.ID));
                _userPortfolioService.Remove(id);
            }
            return Json(new { success = true }, JsonRequestBehavior.AllowGet);
            
        }

        [HttpPost]
        public ActionResult DeleteSymbol(int portfolioId, string symbol)
        {
            if (portfolioId > 0)
            {
                UserSymbol dbItem = _userSymbolService.GetAll().Where(x => x.Portfolio.ID == portfolioId && x.SymbolCode == symbol).FirstOrDefault();
                if (dbItem != null)
                    _userSymbolService.Remove(dbItem.ID);
            }
            else
            {
                CompanyInfo ci = InfoList.Where(x => x.Symbol == symbol).FirstOrDefault();
                if (ci != null)
                    InfoList.Remove(ci);
            }

            return Json(new { success = true, id = portfolioId }, JsonRequestBehavior.AllowGet);
        }

        [HttpPost]
        public ActionResult GetSymbolData(PortfoliosViewModel model)
        {
            if (string.IsNullOrEmpty(model.NameCompany))
                return Json(new { success = false, message = "Company name required!" }, JsonRequestBehavior.AllowGet);

            bool isFound;
            ParseCompany(model.SelectedPortfolioId, model.NameCompany, out isFound);

            if (!isFound)
            {
                return Json(new { success = false, message = "Symbol not found!" }, JsonRequestBehavior.AllowGet);
            }

            if (model.SelectedPortfolioId > 0)
            {
                if (isFound)
                {
                    UserPortfolio dbItem = _userPortfolioService.Get(model.SelectedPortfolioId);
                    if (!dbItem.Symbols.Any(x => x.SymbolCode.ToLower() == model.NameCompany.ToLower()))
                    {
                        UserSymbol dbItemSub = new UserSymbol();
                        dbItemSub.SymbolCode = model.NameCompany;
                        dbItemSub.Status = 1;
                        dbItem.Symbols.Add(dbItemSub);

                        _userPortfolioService.Update(dbItem);
                    }
                }
            }
            else
            {
                if (isFound)
                {
                    CompanyInfo ci = new CompanyInfo();
                    ci.Symbol = model.NameCompany;
                    if (!InfoList.Any(x => x.Symbol.ToLower() == model.NameCompany.ToLower()))
                        InfoList.Add(ci);
                }
            }
            
            return Json(new { success = true }, JsonRequestBehavior.AllowGet);
        }

        public ActionResult GetSymbols(int id)
        {
            List<CompanyInfo> model = new List<CompanyInfo>();

            bool isFound;
            if (id > 0)
            {
                UserPortfolio selected = _userPortfolioService.Get(id);

                List<UserSymbol> items = selected.Symbols.OrderBy(x => x.SymbolCode).ToList();
                foreach (UserSymbol item in items)
                    model.Add(ParseCompany(id, item.SymbolCode, out isFound));
            }
            else
            {
                foreach (CompanyInfo item in InfoList)
                    model.Add(ParseCompany(id, item.Symbol, out isFound));
            }

            return PartialView("CompanyInfoList", model);
        }

        public ActionResult GetSymbolsOptionsHystory(PortfoliosViewModel model)
        {
            if (model == null)
                model = new PortfoliosViewModel();

            if (!string.IsNullOrEmpty(model.SymbolsList))
                model.Hystory = _symbolOptionsHistoryService.Search(model.IsCallsFilter, model.DaysInTradeFilter, model.MinDiscountFilter, model.MinAnnualReturnFieldFilter, model.MinAnnualReturnFilter, model.MinProbabilityFilter, model.SymbolsList, model.SortOrderSymbolOptionsHystory);

            return PartialView("SymbolOptionsHistoryList", model.Hystory);
        }
	
		[HttpPost]
		public ActionResult BuyProduct(byte producttype)
		{
			string message = "Success";

    		_subscriberService.Order(User.Identity.Name, producttype);

			return Json(new {message}, JsonRequestBehavior.AllowGet);
		}

		[HttpPost]
		public ActionResult UnsubscribeSMM()
		{
            string message = "Unsubscribed Successfully";

			Subscriber subscriber = _subscriberService.Get(User.Identity.Name);

			_subscriberService.Unsubscribe(subscriber.ID);
			return Json(new {message}, JsonRequestBehavior.AllowGet);
		}
		
		
		[HttpPost]
		public ActionResult SubscribeMMM()
		{
			string message = "Subscribed successfull";

			Subscriber subscriber = _subscriberService.Get(User.Identity.Name);

			_subscriberService.SubscribeMMM(subscriber);
			return Json(new {message}, JsonRequestBehavior.AllowGet);
		}

		[HttpPost]
		public ActionResult SubscribeSMMFree()
		{
            string message = "Subscribed Successfully";

			var subscriber = _subscriberService.Get(User.Identity.Name);
			_subscriberService.SubscribeSMMFree(subscriber);

			
			return Json(new {message}, JsonRequestBehavior.AllowGet);
		}

		[HttpPost]
		public ActionResult UnsubscribeMMM()
		{
            string message = "Unsubscribed Successfully";

			Subscriber subscriber = _subscriberService.Get(User.Identity.Name);

			_subscriberService.UnsubscribeMMM(subscriber);
			return Json(new { message }, JsonRequestBehavior.AllowGet);
		}

        [HttpGet]
        public ActionResult GotoPayPal()
        {
            return View();
        }
		
		[HttpGet]
		public PartialViewResult PayPalForm()
		{
			PayPalViewModel model = GetPayPalModel(_subscriberService.GetOrder(User.Identity.Name));
            return PartialView(model);
		}


		public ActionResult BalanceDue()
		{
			var order = _subscriberService.GetOrder(User.Identity.Name);
			var model = new BallanceDueViewModel();
			model.CostDue = order.Price;
			model.ProductName = _subscriberService.GetProducts(order.Subscriber).First(x => x.Id == order.ProductId).ProductName;
            
			return PartialView(model);
		}

		[HttpPost]
		public ActionResult CancelChanges()
		{
			string message = "Cancel successfull";
			try
			{
				_subscriberService.Cancel(User.Identity.Name);
			}
			catch (Exception ex)
			{
				message = "Cancel failed. Unexpected error has occured";
				//new LoggingUtility().WriteErrorToLog(ex);
                _logService.Add(ex);
			}
			return Json(new {message}, JsonRequestBehavior.AllowGet);
		}

		[HttpGet]
		public ActionResult ChangePassword()
		{
			return View(new ChangePasswordViewModel());
		}

		[HttpPost]
		public ActionResult ChangePassword(ChangePasswordViewModel model)
		{
			if (ModelState.IsValid)
			{
				Subscriber subscriber = _subscriberService.Get(User.Identity.Name);
				subscriber.PasswordHash = PasswordUtility.GetPasswordHash(model.Password);

				_subscriberService.Update(subscriber);

				model.IsSuccess = true;
			}

			return View(model);
		}

        [HttpPost]
        public ActionResult ApplyPromoCode(string promocode)
        {
            string message = String.Empty;
            if (String.IsNullOrEmpty(promocode))
            {
                message = "Enter promocode, please";
                return Json(new { message}, JsonRequestBehavior.AllowGet);
            }

            if (_subscriberService.GetOrderBySubscriberAndPromoCode(User.Identity.Name, promocode) != null)
            {
                message = "You have already used this code";
                return Json(new { message }, JsonRequestBehavior.AllowGet);
            }

            Promotion promo = _promotionService.GetPromoByCode(promocode);
            if (promo == null)
            {
                message = "Code is not valid";
                return Json(new { message }, JsonRequestBehavior.AllowGet);
            }
            
            Order order = _subscriberService.GetOrder(User.Identity.Name);
            if (order == null)
            {
                 message = "Order not found";
                return Json(new { message }, JsonRequestBehavior.AllowGet);
            }

            // Date validation
            DateTime dtToday = DateTime.Today.Date;
            if ((dtToday < promo.EffectiveDate) || (dtToday > promo.ExpirationDate))
            {
                message = "Code is not valid";
                return Json(new { message }, JsonRequestBehavior.AllowGet);
            }
            // Product validation
            switch ((PromotionProduct)promo.Product)
            {
                case PromotionProduct.Webinar:
                    message = "Code is not valid";
                    return Json(new { message }, JsonRequestBehavior.AllowGet);
                    break;
                case PromotionProduct.SubscriptionRenewal:
                    if (order.ProductId != CoreConstants.RENEW_PRODUCT)
                    {
                        message = "Code is not valid";
                        return Json(new { message }, JsonRequestBehavior.AllowGet);
                    }
                    break;
                case PromotionProduct.GoldSubscription:
                    if (order.ProductId != CoreConstants.UPGRADE_TO_GOLD_PRODUCT)
                    {
                        message = "Code is not valid";
                        return Json(new { message }, JsonRequestBehavior.AllowGet);
                    }
                    break;
                case PromotionProduct.SilverSubscription:
                    if (order.ProductId != CoreConstants.UPGRADE_TO_SILVER_PRODUCT)
                    {
                        message = "Code is not valid";
                        return Json(new { message }, JsonRequestBehavior.AllowGet);
                    }
                    break;
                case PromotionProduct.AllSubscriptionLevels:
                    if (order.ProductId == CoreConstants.RENEW_PRODUCT)
                    {
                        message = "Code is not valid";
                        return Json(new { message }, JsonRequestBehavior.AllowGet);
                    }
                    break;
                case PromotionProduct.PlatinumSubscription:
                    if (order.ProductId != CoreConstants.UPGRADE_TO_PLATINUM_PRODUCT)
                    {
                        message = "Code is not valid";
                        return Json(new { message }, JsonRequestBehavior.AllowGet);
                    }
                    break;
            }

            double price = CalculateNewPrice(order.Price, promo.Discount, promo.IsPercent);

            try
            {
                order.PromoCode = promocode;
                order.Price = price;
                var updatedOrder = _subscriberService.UpdateOrder(order);
                price = updatedOrder.Price;
            }
            catch (Exception ex)
            {
                message = "Code is not valid for this order";
                //new LoggingUtility().WriteErrorToLog(ex);
                _logService.Add(ex);
            }
            return Json(new {message, price}, JsonRequestBehavior.AllowGet);
        }
		

		private PayPalViewModel GetPayPalModel(Order order)
		{
			
			return new PayPalViewModel
			{
				PayPalServerUrl =
					string.Equals(WebConfigurationManager.AppSettings["isPayPalSandbox"], "true",
						StringComparison.InvariantCultureIgnoreCase)
						? "https://www.sandbox.paypal.com/us/cgi-bin/webscr"
						: "https://www.paypal.com/us/cgi-bin/webscr",
				BusinessAccount = WebConfigurationManager.AppSettings["PayPalBizAccount"],
				NotifyUrl = WebConfigurationManager.AppSettings["NotifyURL"],
				ReturnUrl = WebConfigurationManager.AppSettings["ReturnURL"],
				CancelUrl = WebConfigurationManager.AppSettings["CancelURL"],
				Custom = order.ID.ToString(),
				ItemName = _subscriberService.GetProducts(order.Subscriber).First(x=> x.Id == order.ProductId).ProductName,
				ItemNumber = ((SubscriptionType) (order.SubscriptionLevel)).ToString(),
				Amount = order.Price
			};
		}

        private List<CompanyInfo> InfoList
        {
            get
            {
                if (Session["InfoList"] == null)
                {
                    Session["InfoList"] = new List<CompanyInfo>();
                }

                return (List<CompanyInfo>)Session["InfoList"];
            }
        }

        [NonAction]
        private CompanyInfo ParseCompany(int portfolioId, string companyName, out bool isFound)
        {
            isFound = false;
            string url = string.Format("http://finance.yahoo.com/q?s={0}", companyName);

            HtmlWeb web = new HtmlWeb();
            HtmlDocument doc = web.Load(url);

            CompanyInfo info = new CompanyInfo();
            info.Symbol = companyName;
            info.Url = url;
            info.PortfolioId = portfolioId;

            var priceNode = doc.DocumentNode.SelectNodes("//span[@class='time_rtq_ticker']");
            if (priceNode != null)
            {
                info.Price = priceNode[0].InnerText;
            }

            var infoNodes = doc.DocumentNode.SelectNodes("//div[@id='yfi_quote_summary_data']");
            if (infoNodes != null)
            {
                foreach (HtmlNode container in infoNodes)
                {
                    isFound = true;

                    HtmlNodeCollection tables = container.SelectNodes("table");
                    for (int j = 0; j < tables.Count; ++j)
                    {
                        HtmlNode table = tables[j];

                        if ((table.Id != "table1") && (table.Id != "table2"))
                            continue;

                        HtmlNodeCollection rows = table.SelectNodes("tr");
                        for (int i = 0; i < rows.Count; ++i)
                        {
                            HtmlNode row = rows[i];

                            HtmlNodeCollection cellsHeader = row.SelectNodes("th");
                            HtmlNodeCollection cellsData = row.SelectNodes("td");

                            string header = cellsHeader[0].InnerText;
                            if (header == "Prev Close:")
                                info.PrevClose = cellsData[0].InnerText;
                            else if (header == "Open:")
                                info.Open = cellsData[0].InnerText;
                            else if (header == "Bid:")
                                info.Bid = cellsData[0].InnerText;
                            else if (header == "Ask:")
                                info.Ask = cellsData[0].InnerText;
                            else if (header == "1y Target Est:")
                                info.TargetEst = cellsData[0].InnerText;
                            else if (header == "Beta:")
                                info.Beta = cellsData[0].InnerText;
                            else if (header == "Next Earnings Date:")
                                info.NextEarningsDate = cellsData[0].InnerText;

                            else if (header == "Day's Range:")
                                info.DaysRange = cellsData[0].InnerText;
                            else if (header == "52wk Range:")
                                info.Wk52Range = cellsData[0].InnerText;
                            else if (header == "Volume:")
                                info.Volume = cellsData[0].InnerText;
                            else if (header == "Avg Vol (3m):")
                                info.AvgVol3m = cellsData[0].InnerText;
                            else if (header == "Market Cap:")
                                info.MarketCap = cellsData[0].InnerText;
                            else if (header == "P/E (ttm):")
                                info.PE = cellsData[0].InnerText;
                            else if (header == "EPS (ttm):")
                                info.EPS = cellsData[0].InnerText;
                            else if (header == "Div &amp; Yield:")
                                info.Div = cellsData[0].InnerText;
                        }
                    }

                    break;
                }
            }
            return info;
        }
	}
}